# mod_loader.gd
extends Node

# ======================================================
# 模组加载系统
# 负责加载和管理游戏模组，支持PCK资源包和脚本模组
# ======================================================

#region 配置常量
# ------------------------------------------------------
# 模组日志文件存储路径
const MOD_LOG_PATH = "user://mods.log"

# 有效模组必须包含的文件清单
const REQUIRED_MOD_FILES = [
    "mod_main.gd", # 模组主脚本
    "manifest.json" # 模组元数据文件
]

# 清单文件必须包含的元数据字段
const REQUIRED_META_TAGS = [
    "id", # 模组唯一标识符
    "name", # 模组显示名称
    "version", # 语义化版本号
    "compatible_game_version", # 兼容的游戏版本
    "authors", # 作者列表
    "description", # 模组功能描述
    "dependencies", # 依赖的其他模组ID
]
#endregion

#region 运行时数据
# ------------------------------------------------------
# 已加载模组数据存储（键：模组ID，值：模组配置字典）
var mod_data = {}

# 模组加载顺序列表（解决依赖关系）
var mod_load_order = []

# 缺失依赖记录（键：模组ID，值：缺失的依赖项列表）
var mod_missing_dependencies = {}
#endregion

#region 初始化流程
# ------------------------------------------------------
# 对象初始化时自动执行
func _init():
    # 加载所有PCK资源包
    _load_pck_files()

    # 读取并验证模组数据
    _read_mods()

    # 初始化所有有效模组
    for mod_id in mod_data:
        var mod = mod_data[mod_id]
        _init_mod(mod)
#endregion

#region 模组初始化
# ------------------------------------------------------
# 初始化单个模组
# @param mod: 包含manifest和主脚本路径的字典
func _init_mod(mod):
    # 加载模组主脚本
    var mod_main_path = mod.mod_main_path
    var mod_main_script = ResourceLoader.load(mod_main_path)

    # 实例化模组并挂载到场景树
    var mod_main_instance = mod_main_script.new()
    mod_main_instance.name = mod.manifest.id
    add_child(mod_main_instance, true)
#endregion

#region 文件系统操作
# ------------------------------------------------------
# 获取模组根目录路径
# @return: res://mods 目录路径
func _get_mod_folder_dir():
    var base_dir = DirAccess.open("res://mods").get_current_dir()
    return base_dir

# 获取指定目录下的文件列表
# @param dir: 目标目录路径
# @return: 文件名到完整路径的映射字典
func _get_mod_files(dir):
    var files = DirAccess.open(dir).get_files()
    var mod_files = {}
    for file in files:
        mod_files[file] = str(dir, '/', file)
    return mod_files
#endregion

#region 模组验证
# ------------------------------------------------------
# 检查模组文件完整性
# @param mod_files: 文件路径字典
# @return: 是否包含所有必需文件
func _check_mod_files(mod_files: Dictionary) -> bool:
    for file in REQUIRED_MOD_FILES:
        if not mod_files.has(file):
            return false
    return true

# 验证清单元数据完整性
# @param manifest: 解析后的清单数据
# @return: 是否包含所有必需字段
func _check_manifest(manifest: Dictionary) -> bool:
    for key in REQUIRED_META_TAGS:
        if not manifest.has(key):
            print("Invalid manifest key: ", key)
            return false
    return true
#endregion

#region 模组加载
# ------------------------------------------------------
# 主加载流程：扫描目录并注册有效模组
func _read_mods():
    var base_dir = _get_mod_folder_dir()
    var dirs = DirAccess.open(base_dir).get_directories()

    # 遍历所有模组目录
    for dir in dirs:
        var full_path = str(base_dir, '/', dir)
        var mod_files = _get_mod_files(full_path)

        # 跳过不完整的模组
        if not _check_mod_files(mod_files):
            continue

        # 解析清单文件
        var manifest_path = mod_files["manifest.json"]
        var manifest_file = FileAccess.open(manifest_path, FileAccess.READ)
        var _manifest = JSON.parse_string(manifest_file.get_as_text())

        # 跳过无效清单
        if not _check_manifest(_manifest):
            continue

        # 注册模组数据
        mod_data[_manifest["id"]] = {
            "manifest": _manifest,
            "mod_main_path": mod_files["mod_main.gd"]
        }

# 加载所有PCK资源包
func _load_pck_files():
    var base_dir = 'mods/'
    var dirs = DirAccess.open(base_dir).get_directories()

    # 遍历所有模组子目录
    for dir in dirs:
        var mod_dir = base_dir + dir
        var files = DirAccess.open(mod_dir).get_files()

        # 加载找到的每个PCK文件
        for file in files:
            if file.get_extension() == 'pck':
                var pck_path = mod_dir + '/' + file
                ProjectSettings.load_resource_pack(pck_path)
#endregion
